import pygame
import numpy as np

# 用键盘模拟弹琴的程序
# 初始化pygame
pygame.init()
pygame.mixer.init(frequency=44100, size=-16, channels=2)

# 生成正弦波音调的函数
def generate_tone(frequency, duration=0.5, volume=0.1):
    sample_rate = 44100
    samples = np.arange(int(duration * sample_rate))
    sound = np.sin(2 * np.pi * frequency * samples / sample_rate)
    sound = np.asarray([sound, sound]).T  # 转换为立体声
    sound = (sound * volume * 32767).astype(np.int16)
    sound = np.ascontiguousarray(sound)  # 确保数组是C连续的
    return pygame.sndarray.make_sound(sound)

# 定义键盘按键与音符频率的映射 (C大调音阶)
key_tones = {
    pygame.K_z: 130.81,  # C3 (低音)
    pygame.K_x: 146.83,  # D3
    pygame.K_c: 164.81,  # E3
    pygame.K_v: 174.61,  # F3
    pygame.K_b: 196.00,  # G3
    pygame.K_n: 220.00,  # A3
    pygame.K_m: 246.94,  # B3
    pygame.K_a: 261.63,  # C4 (中音)
    pygame.K_s: 293.66,  # D4
    pygame.K_d: 329.63,  # E4
    pygame.K_f: 349.23,  # F4
    pygame.K_g: 392.00,  # G4
    pygame.K_h: 440.00,  # A4
    pygame.K_j: 493.88,  # B4
    pygame.K_q: 523.25,  # C5 (高音)
    pygame.K_w: 587.33,  # D5
    pygame.K_e: 659.25,  # E5
    pygame.K_r: 698.46,  # F5
    pygame.K_t: 783.99,  # G5
    pygame.K_y: 880.00,  # A5
    pygame.K_u: 987.77   # B5
}

# 预先生成所有音调
tones = {key: generate_tone(freq) for key, freq in key_tones.items()}

# 创建一个窗口
screen = pygame.display.set_mode((450, 400))
pygame.display.set_caption('Piano Simulator')
# 初始化字体
font = pygame.font.SysFont('Arial', 24)

# 定义键盘布局和颜色
key_colors = {
    'normal': (220, 220, 220),
    'pressed': (255, 200, 0)
}

# 键盘布局位置和大小
key_positions = {
    # 高音区 (第一行)
    pygame.K_q: (50, 100, 40, 80), pygame.K_w: (100, 100, 40, 80), pygame.K_e: (150, 100, 40, 80),
    pygame.K_r: (200, 100, 40, 80), pygame.K_t: (250, 100, 40, 80), pygame.K_y: (300, 100, 40, 80),
    pygame.K_u: (350, 100, 40, 80),
    
    # 中音区 (第二行)
    pygame.K_a: (50, 200, 40, 80), pygame.K_s: (100, 200, 40, 80), pygame.K_d: (150, 200, 40, 80),
    pygame.K_f: (200, 200, 40, 80), pygame.K_g: (250, 200, 40, 80), pygame.K_h: (300, 200, 40, 80),
    pygame.K_j: (350, 200, 40, 80),
    
    # 低音区 (第三行)
    pygame.K_z: (50, 300, 40, 80), pygame.K_x: (100, 300, 40, 80), pygame.K_c: (150, 300, 40, 80),
    pygame.K_v: (200, 300, 40, 80), pygame.K_b: (250, 300, 40, 80), pygame.K_n: (300, 300, 40, 80),
    pygame.K_m: (350, 300, 40, 80)
}

# 绘制键盘布局的函数
def draw_keyboard():
    for key, rect in key_positions.items():
        color = key_colors['pressed'] if key == current_key else key_colors['normal']
        pygame.draw.rect(screen, color, rect)
        pygame.draw.rect(screen, (0, 0, 0), rect, 2)
        
        # 绘制按键标签
        key_name = pygame.key.name(key)
        label = font.render(key_name, True, (0, 0, 0))
        screen.blit(label, (rect[0] + 10, rect[1] + 10))
        
        # 绘制音阶名称
        note_names = {
            pygame.K_z: "C3", pygame.K_x: "D3", pygame.K_c: "E3", pygame.K_v: "F3",
            pygame.K_b: "G3", pygame.K_n: "A3", pygame.K_m: "B3", pygame.K_a: "C4",
            pygame.K_s: "D4", pygame.K_d: "E4", pygame.K_f: "F4", pygame.K_g: "G4",
            pygame.K_h: "A4", pygame.K_j: "B4", pygame.K_q: "C5", pygame.K_w: "D5",
            pygame.K_e: "E5", pygame.K_r: "F5", pygame.K_t: "G5", pygame.K_y: "A5",
            pygame.K_u: "B5"
        }
        note_label = font.render(note_names.get(key, ""), True, (0, 0, 0))
        screen.blit(note_label, (rect[0] + 10, rect[1] + 40))

running = True
current_key = None
current_note = None
while running:
    screen.fill((255, 255, 255))  # 清空屏幕
    
    # 绘制键盘布局
    draw_keyboard()
    
    # 显示当前按键和音阶
    if current_key:
        key_text = font.render(f"按键: {pygame.key.name(current_key)}", True, (0, 0, 0))
        note_text = font.render(f"音阶: {current_note}", True, (0, 0, 0))
        screen.blit(key_text, (20, 20))
        note_pos = (100, 20)
        screen.blit(note_text, note_pos)
        
        # 显示频率
        freq_text = font.render(f"频率: {key_tones[current_key]}Hz", True, (0, 0, 0))
        screen.blit(freq_text, (180, 20))
    
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.KEYDOWN:
            if event.key in tones:
                tones[event.key].play()
                current_key = event.key
                # 获取音阶名称
                note_names = {
                    pygame.K_z: "C3", pygame.K_x: "D3", pygame.K_c: "E3", pygame.K_v: "F3",
                    pygame.K_b: "G3", pygame.K_n: "A3", pygame.K_m: "B3", pygame.K_a: "C4",
                    pygame.K_s: "D4", pygame.K_d: "E4", pygame.K_f: "F4", pygame.K_g: "G4",
                    pygame.K_h: "A4", pygame.K_j: "B4", pygame.K_q: "C5", pygame.K_w: "D5",
                    pygame.K_e: "E5", pygame.K_r: "F5", pygame.K_t: "G5", pygame.K_y: "A5",
                    pygame.K_u: "B5"
                }
                current_note = note_names.get(event.key)
    
    pygame.display.flip()  # 更新屏幕显示

# 退出 Pygame
pygame.quit()